//! 轮转文件 Appender
//!
//! 支持基于大小的文件轮转，当日志文件达到指定大小时自动轮转。
//!
//! ## 主要类型
//!
//! - [`RotatingFileAppender`] - 支持轮转的文件输出 Appender
//!
//! ## 示例
//!
//! ```rust,ignore
//! use log4r::appender::RotatingFileAppender;
//! use log4r::Level;
//!
//! // 创建 10MB 轮转的文件 Appender
//! let appender = RotatingFileAppender::new("logs/app.log", 10 * 1024 * 1024)?;
//!
//! // 自定义配置
//! let appender = RotatingFileAppender::new("logs/app.log", 10 * 1024 * 1024)?
//!     .with_level(Level::Debug);
//! ```

use std::fs::{File, OpenOptions};
use std::io::{BufWriter, Write};
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Mutex;

use crate::appender::Appender;
use crate::config::types::RotationPeriod;
use crate::error::Log4rError;
use crate::event::LogEvent;
use crate::formatter::{Formatter, TextFormatter};
use crate::level::Level;

/// 默认最大文件大小：10MB
const DEFAULT_MAX_SIZE: u64 = 10 * 1024 * 1024;

/// 支持轮转的文件日志输出器
///
/// 支持基于大小和/或时间的文件轮转：
/// - 大小轮转：当日志文件达到指定大小时自动轮转
/// - 时间轮转：按小时/天/周自动轮转
/// - 双触发：同时启用大小和时间轮转，任一条件满足即触发
///
/// 轮转后的文件命名格式：`{stem}.{date}.{seq}.{ext}`
///
/// # 线程安全
///
/// 使用 `Mutex<BufWriter<File>>` 保护文件操作，使用 `AtomicU64` 跟踪文件大小，
/// 实现 `Send + Sync`，可在多线程环境中安全使用。
///
/// # 示例
///
/// ```rust,ignore
/// use log4r::appender::RotatingFileAppender;
/// use log4r::{Level, RotationPeriod};
///
/// // 大小轮转
/// let appender = RotatingFileAppender::new("logs/app.log", 10 * 1024 * 1024)?
///     .with_level(Level::Info);
///
/// // 时间轮转
/// let appender = RotatingFileAppender::with_time_rotation("logs/app.log", RotationPeriod::Daily)?;
///
/// // 大小+时间双触发
/// let appender = RotatingFileAppender::with_config("logs/app.log", Some(10 * 1024 * 1024), RotationPeriod::Daily)?;
/// ```
pub struct RotatingFileAppender {
    /// 日志文件路径
    path: PathBuf,
    /// 最大文件大小（字节），None 表示不启用大小轮转
    max_size: Option<u64>,
    /// 当前文件大小（内存计数器，避免 I/O）
    current_size: AtomicU64,
    /// 时间轮转周期
    rotation_period: RotationPeriod,
    /// 上次轮转时间（用于时间轮转检测）
    last_rotation_time: Mutex<time::OffsetDateTime>,
    /// 历史文件数量限制，None 使用默认值 7，0 表示禁用清理
    max_files: Option<u32>,
    /// 日志级别过滤
    level: Level,
    /// 格式化器
    formatter: Box<dyn Formatter>,
    /// 带缓冲的文件写入器（Mutex 保护）
    writer: Mutex<BufWriter<File>>,
}

// 编译时断言：验证 RotatingFileAppender 实现 Send + Sync
const _: () = {
    const fn assert_send_sync<T: Send + Sync>() {}
    assert_send_sync::<RotatingFileAppender>();
};

impl RotatingFileAppender {
    /// 创建新的 RotatingFileAppender（仅大小轮转）
    ///
    /// # Arguments
    /// * `path` - 日志文件路径（支持相对路径和绝对路径）
    /// * `max_size` - 触发轮转的最大文件大小（字节）
    ///
    /// # Returns
    /// * `Ok(RotatingFileAppender)` - 创建成功
    /// * `Err(Log4rError::IoError)` - 文件创建失败
    ///
    /// # 行为
    /// - 自动创建文件所在目录（如果不存在）
    /// - 以追加模式打开文件（不覆盖已有内容）
    /// - 初始化时读取现有文件大小设置计数器
    /// - 默认级别过滤：Trace（不过滤任何日志）
    /// - 默认格式化器：TextFormatter
    pub fn new(path: impl AsRef<Path>, max_size: u64) -> Result<Self, Log4rError> {
        Self::with_config(path, Some(max_size), RotationPeriod::None)
    }

    /// 创建仅时间轮转的 RotatingFileAppender
    ///
    /// # Arguments
    /// * `path` - 日志文件路径
    /// * `period` - 时间轮转周期
    ///
    /// # Returns
    /// * `Ok(RotatingFileAppender)` - 创建成功
    /// * `Err(Log4rError::IoError)` - 文件创建失败
    ///
    /// # 示例
    ///
    /// ```rust,ignore
    /// use log4r::appender::RotatingFileAppender;
    /// use log4r::RotationPeriod;
    ///
    /// let appender = RotatingFileAppender::with_time_rotation("logs/app.log", RotationPeriod::Daily)?;
    /// ```
    pub fn with_time_rotation(
        path: impl AsRef<Path>,
        period: RotationPeriod,
    ) -> Result<Self, Log4rError> {
        Self::with_config(path, None, period)
    }

    /// 创建完整配置的 RotatingFileAppender
    ///
    /// # Arguments
    /// * `path` - 日志文件路径
    /// * `max_size` - 最大文件大小（字节），None 表示不启用大小轮转
    /// * `period` - 时间轮转周期
    /// * `max_files` - 历史文件数量限制，None 使用默认值 7，Some(0) 禁用清理
    ///
    /// # Returns
    /// * `Ok(RotatingFileAppender)` - 创建成功
    /// * `Err(Log4rError::IoError)` - 文件创建失败
    pub fn with_config(
        path: impl AsRef<Path>,
        max_size: Option<u64>,
        period: RotationPeriod,
    ) -> Result<Self, Log4rError> {
        Self::with_full_config(path, max_size, period, None)
    }

    /// 创建完整配置的 RotatingFileAppender（包含 max_files）
    ///
    /// # Arguments
    /// * `path` - 日志文件路径
    /// * `max_size` - 最大文件大小（字节），None 表示不启用大小轮转
    /// * `period` - 时间轮转周期
    /// * `max_files` - 历史文件数量限制，None 使用默认值 7，Some(0) 禁用清理
    ///
    /// # Returns
    /// * `Ok(RotatingFileAppender)` - 创建成功
    /// * `Err(Log4rError::IoError)` - 文件创建失败
    pub fn with_full_config(
        path: impl AsRef<Path>,
        max_size: Option<u64>,
        period: RotationPeriod,
        max_files: Option<u32>,
    ) -> Result<Self, Log4rError> {
        let path = path.as_ref().to_path_buf();

        // 自动创建父目录
        if let Some(parent) = path.parent() {
            if !parent.as_os_str().is_empty() && !parent.exists() {
                std::fs::create_dir_all(parent).map_err(Log4rError::IoError)?;
            }
        }

        // 读取现有文件大小
        let current_size = if path.exists() {
            match std::fs::metadata(&path) {
                Ok(m) => m.len(),
                Err(e) => {
                    eprintln!(
                        "[log4r] 警告: 无法读取文件元数据 ({}): {}，假设文件大小为 0",
                        path.display(),
                        e
                    );
                    0
                }
            }
        } else {
            0
        };

        // 以追加模式打开文件
        let file = OpenOptions::new()
            .create(true)
            .append(true)
            .open(&path)
            .map_err(Log4rError::IoError)?;

        let writer = BufWriter::new(file);

        // 初始化 last_rotation_time
        // 优先使用本地时间，失败时降级使用 UTC
        let now = match time::OffsetDateTime::now_local() {
            Ok(t) => t,
            Err(_) => {
                eprintln!("[log4r] 警告: 无法获取本地时间，使用 UTC 时间");
                time::OffsetDateTime::now_utc()
            }
        };

        Ok(Self {
            path,
            max_size,
            current_size: AtomicU64::new(current_size),
            rotation_period: period,
            last_rotation_time: Mutex::new(now),
            max_files,
            level: Level::Trace,
            formatter: Box::new(TextFormatter::new()),
            writer: Mutex::new(writer),
        })
    }

    /// 使用默认最大大小（10MB）创建 RotatingFileAppender
    ///
    /// # Arguments
    /// * `path` - 日志文件路径
    ///
    /// # Returns
    /// * `Ok(RotatingFileAppender)` - 创建成功
    /// * `Err(Log4rError)` - 创建失败
    pub fn with_default_size(path: impl AsRef<Path>) -> Result<Self, Log4rError> {
        Self::new(path, DEFAULT_MAX_SIZE)
    }

    /// 设置日志级别过滤
    ///
    /// 低于此级别的日志将被忽略，不写入文件。
    pub fn with_level(mut self, level: Level) -> Self {
        self.level = level;
        self
    }

    /// 设置自定义格式化器
    pub fn with_formatter(mut self, formatter: Box<dyn Formatter>) -> Self {
        self.formatter = formatter;
        self
    }

    /// 设置时间轮转周期（Builder 方法）
    pub fn with_rotation_period(mut self, period: RotationPeriod) -> Self {
        self.rotation_period = period;
        self
    }

    /// 获取文件路径
    pub fn path(&self) -> &Path {
        &self.path
    }

    /// 获取当前级别过滤设置
    pub fn level(&self) -> Level {
        self.level
    }

    /// 获取最大文件大小
    pub fn max_size(&self) -> Option<u64> {
        self.max_size
    }

    /// 获取当前文件大小（内存计数器值）
    pub fn current_size(&self) -> u64 {
        self.current_size.load(Ordering::Relaxed)
    }

    /// 获取时间轮转周期
    pub fn rotation_period(&self) -> RotationPeriod {
        self.rotation_period
    }

    /// 获取历史文件数量限制
    ///
    /// 返回配置的 max_files 值，None 表示使用默认值 7
    pub fn max_files(&self) -> Option<u32> {
        self.max_files
    }

    /// 设置历史文件数量限制（Builder 方法）
    ///
    /// # Arguments
    /// * `max_files` - 历史文件数量限制，0 表示禁用清理
    ///
    /// # Returns
    /// Self，支持链式调用
    pub fn with_max_files(mut self, max_files: u32) -> Self {
        self.max_files = Some(max_files);
        self
    }

    /// 检查两个时间是否在同一周期内
    ///
    /// # Arguments
    /// * `t1` - 第一个时间
    /// * `t2` - 第二个时间
    /// * `period` - 轮转周期
    ///
    /// # Returns
    /// * `true` - 在同一周期内
    /// * `false` - 不在同一周期内
    fn is_same_period(
        t1: time::OffsetDateTime,
        t2: time::OffsetDateTime,
        period: RotationPeriod,
    ) -> bool {
        match period {
            RotationPeriod::None => true, // 不启用时间轮转，始终返回 true
            RotationPeriod::Hourly => {
                (t1.year(), t1.ordinal(), t1.hour()) == (t2.year(), t2.ordinal(), t2.hour())
            }
            RotationPeriod::Daily => (t1.year(), t1.ordinal()) == (t2.year(), t2.ordinal()),
            RotationPeriod::Weekly => {
                // 使用 ISO week year 进行比较，处理跨年边界
                let (y1, w1, _) = t1.to_iso_week_date();
                let (y2, w2, _) = t2.to_iso_week_date();
                (y1, w1) == (y2, w2)
            }
        }
    }

    /// 检查是否需要时间轮转
    ///
    /// # Arguments
    /// * `now` - 当前时间
    ///
    /// # Returns
    /// * `true` - 需要轮转
    /// * `false` - 不需要轮转
    fn should_rotate_by_time(&self, now: time::OffsetDateTime) -> bool {
        if self.rotation_period == RotationPeriod::None {
            return false;
        }

        // 获取 last_rotation_time 锁
        let last_time = match self.last_rotation_time.lock() {
            Ok(guard) => *guard,
            Err(_) => return false, // 锁中毒，不轮转
        };

        !Self::is_same_period(last_time, now, self.rotation_period)
    }

    /// 获取日期前缀（用于轮转文件命名）
    ///
    /// # Arguments
    /// * `time` - 时间
    ///
    /// # Returns
    /// 日期前缀字符串
    fn get_date_prefix(&self, time: time::OffsetDateTime) -> String {
        match self.rotation_period {
            RotationPeriod::None | RotationPeriod::Daily => {
                format!(
                    "{:04}-{:02}-{:02}",
                    time.year(),
                    time.month() as u8,
                    time.day()
                )
            }
            RotationPeriod::Hourly => {
                format!(
                    "{:04}-{:02}-{:02}-{:02}",
                    time.year(),
                    time.month() as u8,
                    time.day(),
                    time.hour()
                )
            }
            RotationPeriod::Weekly => {
                // 使用 to_iso_week_date() 获取正确的 ISO week year
                // 例如：2024-12-30 属于 ISO week 2025-W01
                let (iso_year, iso_week, _) = time.to_iso_week_date();
                format!("{:04}-W{:02}", iso_year, iso_week)
            }
        }
    }

    /// 生成轮转文件名
    ///
    /// 格式: `{stem}.{date_prefix}.{seq}.{ext}` 或 `{stem}.{date_prefix}.{seq}`（无扩展名）
    ///
    /// # Arguments
    /// * `date` - 日期字符串
    /// * `seq` - 序号
    fn generate_rotated_filename(&self, date: &str, seq: u32) -> PathBuf {
        let stem = self
            .path
            .file_stem()
            .and_then(|s| s.to_str())
            .unwrap_or("log");

        let ext = self.path.extension().and_then(|s| s.to_str());

        let filename = match ext {
            Some(e) => format!("{}.{}.{}.{}", stem, date, seq, e),
            None => format!("{}.{}.{}", stem, date, seq),
        };

        self.path.with_file_name(filename)
    }

    /// 获取当天最大序号
    ///
    /// 扫描目录中同一天的轮转文件，返回最大序号
    fn get_max_seq_for_date(&self, date: &str) -> u32 {
        let parent = match self.path.parent() {
            Some(p) if !p.as_os_str().is_empty() => p,
            _ => Path::new("."),
        };

        let stem = self
            .path
            .file_stem()
            .and_then(|s| s.to_str())
            .unwrap_or("log");

        let prefix = format!("{}.{}.", stem, date);

        let mut max_seq = 0u32;

        if let Ok(entries) = std::fs::read_dir(parent) {
            for entry in entries.flatten() {
                if let Some(name) = entry.file_name().to_str() {
                    if name.starts_with(&prefix) {
                        // 解析序号：{stem}.{date}.{seq}.{ext} 或 {stem}.{date}.{seq}
                        let rest = &name[prefix.len()..];
                        // 取第一个 '.' 之前的部分作为序号
                        let seq_str = rest.split('.').next().unwrap_or("0");
                        if let Ok(seq) = seq_str.parse::<u32>() {
                            max_seq = max_seq.max(seq);
                        }
                    }
                }
            }
        }

        max_seq
    }

    /// 执行轮转操作
    ///
    /// 1. flush 并关闭当前文件
    /// 2. 重命名当前文件为轮转文件名
    /// 3. 创建新的日志文件
    /// 4. 重置 current_size 计数器
    /// 5. 更新 last_rotation_time（如果是时间轮转）
    ///
    /// # 降级策略
    /// 如果轮转失败（重命名或创建新文件失败），继续写入当前文件并输出警告到 stderr
    fn rotate(
        &self,
        writer: &mut BufWriter<File>,
        now: time::OffsetDateTime,
    ) -> Result<(), Log4rError> {
        // 1. flush 当前 writer
        writer.flush().map_err(Log4rError::IoError)?;

        // 2. 获取日期前缀（根据轮转周期）
        let date_prefix = self.get_date_prefix(now);

        // 3. 获取当前周期最大序号并生成新文件名
        let max_seq = self.get_max_seq_for_date(&date_prefix);
        let rotated_path = self.generate_rotated_filename(&date_prefix, max_seq + 1);

        // 4. 重命名当前文件（降级处理）
        if let Err(e) = std::fs::rename(&self.path, &rotated_path) {
            eprintln!(
                "[log4r] 警告: 轮转文件重命名失败 ({} -> {}): {}，继续写入当前文件",
                self.path.display(),
                rotated_path.display(),
                e
            );
            return Ok(()); // 降级：继续写入当前文件
        }

        // 5. 创建新文件（降级处理）
        let new_file = match OpenOptions::new()
            .create(true)
            .append(true)
            .open(&self.path)
        {
            Ok(f) => f,
            Err(e) => {
                eprintln!(
                    "[log4r] 警告: 创建新日志文件失败 ({}): {}，尝试恢复原文件",
                    self.path.display(),
                    e
                );
                // 尝试恢复：将轮转文件重命名回来
                let _ = std::fs::rename(&rotated_path, &self.path);
                return Ok(()); // 降级：继续写入当前文件
            }
        };

        // 6. 替换 writer
        *writer = BufWriter::new(new_file);

        // 7. 重置计数器
        self.current_size.store(0, Ordering::Relaxed);

        // 8. 更新 last_rotation_time
        if let Ok(mut last_time) = self.last_rotation_time.lock() {
            *last_time = now;
        }

        Ok(())
    }

    /// 测试辅助：直接设置 last_rotation_time
    ///
    /// 此方法仅用于测试，不应在生产代码中使用。
    #[doc(hidden)]
    pub fn set_last_rotation_time_for_test(&self, time: time::OffsetDateTime) {
        if let Ok(mut last_time) = self.last_rotation_time.lock() {
            *last_time = time;
        }
    }

    /// 列出所有匹配的轮转文件
    ///
    /// 匹配模式：{stem}.{date}.{seq}.{ext} 或 {stem}.{date}.{seq}（无扩展名）
    /// 也包含压缩文件：{stem}.{date}.{seq}.{ext}.gz, {stem}.{date}.{seq}.{ext}.zst
    ///
    /// # Returns
    /// 匹配的轮转文件路径列表（不含当前活动文件）
    fn list_rotated_files(&self) -> Vec<PathBuf> {
        let parent = match self.path.parent() {
            Some(p) if !p.as_os_str().is_empty() => p,
            _ => Path::new("."),
        };

        let stem = self
            .path
            .file_stem()
            .and_then(|s| s.to_str())
            .unwrap_or("log");

        let ext = self.path.extension().and_then(|s| s.to_str());

        // 构建匹配前缀：{stem}.
        let prefix = format!("{}.", stem);

        let mut files = Vec::new();

        // 扫描目录，失败时输出警告并返回空列表
        let entries = match std::fs::read_dir(parent) {
            Ok(e) => e,
            Err(e) => {
                eprintln!(
                    "[log4r] 警告: 扫描目录失败 ({}): {}，跳过清理",
                    parent.display(),
                    e
                );
                return files;
            }
        };

        for entry in entries.flatten() {
            let path = entry.path();
            let name = entry.file_name();
            let name_str = match name.to_str() {
                Some(s) => s,
                None => continue,
            };

            // 跳过当前活动文件
            if path == self.path {
                continue;
            }

            // 检查前缀匹配
            if !name_str.starts_with(&prefix) {
                continue;
            }

            // 获取前缀后的部分：{date}.{seq}[.{ext}][.gz|.zst]
            let rest = &name_str[prefix.len()..];
            let parts: Vec<&str> = rest.split('.').collect();

            // 至少需要 date 和 seq 两部分
            if parts.len() < 2 {
                continue;
            }

            // 检查扩展名匹配（包括压缩后缀）
            let matches = match ext {
                Some(e) => {
                    // 有扩展名：检查是否以 .{ext} 或 .{ext}.gz 或 .{ext}.zst 结尾
                    name_str.ends_with(&format!(".{}", e))
                        || name_str.ends_with(&format!(".{}.gz", e))
                        || name_str.ends_with(&format!(".{}.zst", e))
                }
                None => {
                    // 无扩展名：检查是否为 {stem}.{date}.{seq} 格式
                    // 或者压缩格式 {stem}.{date}.{seq}.gz / .zst
                    parts.len() == 2
                        || (parts.len() == 3 && (parts[2] == "gz" || parts[2] == "zst"))
                }
            };

            if matches {
                files.push(path);
            }
        }

        files
    }

    /// 清理超出限制的旧文件
    ///
    /// 此方法应在 Mutex 锁外调用，避免阻塞日志写入。
    /// max_files = 0 表示禁用清理，未配置时不执行清理（Story 10-1 行为）。
    /// Story 10-3 完成后，未配置时将使用默认值 7。
    fn cleanup_old_files(&self) {
        // Story 10-1: 只有显式配置 max_files 时才执行清理
        // 这确保轮转过程不会因默认清理而丢失日志
        let max_files = match self.max_files {
            Some(0) => return, // 显式禁用清理
            Some(n) => n as usize,
            None => return, // 未配置时不清理，保持 Story 10-1 行为
        };

        // 获取所有轮转文件
        let mut files = self.list_rotated_files();

        // 未超出限制，无需清理
        if files.len() <= max_files {
            return;
        }

        // 按修改时间排序（最旧的在前），metadata 失败时按文件名排序
        files.sort_by(|a, b| {
            let time_a = std::fs::metadata(a).and_then(|m| m.modified()).ok();
            let time_b = std::fs::metadata(b).and_then(|m| m.modified()).ok();

            match (time_a, time_b) {
                (Some(ta), Some(tb)) => ta.cmp(&tb),
                (Some(_), None) => std::cmp::Ordering::Greater, // 有时间的排后面（保留）
                (None, Some(_)) => std::cmp::Ordering::Less,    // 无时间的排前面（优先删除）
                (None, None) => a.file_name().cmp(&b.file_name()), // 按文件名排序
            }
        });

        // 计算需要删除的文件数量
        let to_delete = files.len() - max_files;

        // 删除最旧的文件（带竞态条件保护）
        for file in files.into_iter().take(to_delete) {
            // 检查文件是否存在，避免并发删除时的竞态条件
            if !file.exists() {
                continue;
            }

            if let Err(e) = std::fs::remove_file(&file) {
                // 忽略 "文件不存在" 错误（可能被其他线程删除）
                if e.kind() != std::io::ErrorKind::NotFound {
                    eprintln!(
                        "[log4r] 警告: 删除旧日志文件失败 ({}): {}",
                        file.display(),
                        e
                    );
                }
                // 继续处理其他文件，不中断
            }
        }
    }
}

impl Appender for RotatingFileAppender {
    fn write(&self, event: &LogEvent) -> Result<(), Log4rError> {
        // 级别过滤：低于阈值的日志直接返回
        if event.level < self.level {
            return Ok(());
        }

        // 格式化日志
        let formatted = self.formatter.format(event);
        let bytes = formatted.as_bytes();
        let bytes_len = bytes.len() as u64 + 1; // +1 for newline

        // 标记是否发生了轮转（用于锁外清理）
        let should_cleanup = {
            // 获取锁
            let mut writer = self.writer.lock().map_err(|_| Log4rError::AppenderError {
                name: "RotatingFileAppender".to_string(),
                cause: "获取文件锁失败".to_string(),
            })?;

            // 获取当前时间（用于时间轮转检测和文件命名）
            let now = match time::OffsetDateTime::now_local() {
                Ok(t) => t,
                Err(_) => time::OffsetDateTime::now_utc(),
            };

            let mut did_rotate = false;

            // 1. 先检查时间轮转（优先）
            if self.should_rotate_by_time(now) {
                if self.rotate(&mut writer, now).is_ok() {
                    did_rotate = true;
                } else {
                    eprintln!(
                        "[log4r] 警告: 时间轮转操作失败 ({}): 继续写入当前文件",
                        self.path.display()
                    );
                }
            }

            // 2. 再检查大小轮转（如果启用）
            if let Some(max_size) = self.max_size {
                let current = self.current_size.load(Ordering::Relaxed);
                if current + bytes_len >= max_size {
                    if self.rotate(&mut writer, now).is_ok() {
                        did_rotate = true;
                    } else {
                        eprintln!(
                            "[log4r] 警告: 大小轮转操作失败 ({}): 继续写入当前文件",
                            self.path.display()
                        );
                    }
                }
            }

            // 写入格式化字符串 + 换行符
            writeln!(writer, "{}", formatted).map_err(Log4rError::IoError)?;

            // 更新计数器
            self.current_size.fetch_add(bytes_len, Ordering::Relaxed);

            did_rotate
        }; // Mutex 锁在这里释放

        // 锁外执行清理（不阻塞后续写入）
        if should_cleanup {
            self.cleanup_old_files();
        }

        Ok(())
    }

    fn flush(&self) -> Result<(), Log4rError> {
        let mut writer = self.writer.lock().map_err(|_| Log4rError::AppenderError {
            name: "RotatingFileAppender".to_string(),
            cause: "获取文件锁失败".to_string(),
        })?;

        writer.flush().map_err(Log4rError::IoError)
    }

    fn close(&self) -> Result<(), Log4rError> {
        // 先刷新缓冲区
        self.flush()?;
        // BufWriter 在 drop 时会自动关闭文件
        Ok(())
    }
}

#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
    use super::*;
    use tempfile::TempDir;

    // ========================================
    // AC-10.1.7: 编译时断言 Send + Sync
    // ========================================

    /// UT-10.1-011: 编译时验证 RotatingFileAppender 实现 Send + Sync
    #[test]
    fn test_rotating_file_appender_is_send_sync() {
        fn assert_send_sync<T: Send + Sync>() {}
        assert_send_sync::<RotatingFileAppender>();
    }

    /// UT-10.1-011b: 验证 RotatingFileAppender 实现 Appender trait
    #[test]
    fn test_rotating_file_appender_implements_appender() {
        fn accept_appender(_: &dyn Appender) {}
        fn accept_boxed_appender(_: Box<dyn Appender>) {}

        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("trait_test.log");

        let appender = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");
        accept_appender(&appender);
        accept_boxed_appender(Box::new(
            RotatingFileAppender::new(temp_dir.path().join("boxed.log"), 1024).unwrap(),
        ));
    }

    // ========================================
    // AC-10.1.6: 默认值测试
    // ========================================

    /// UT-10.1-006: 测试默认最大大小为 10MB (AC-10.1.6 #11)
    #[test]
    fn test_default_max_size() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("default_size.log");

        let appender = RotatingFileAppender::with_default_size(&log_path).expect("创建失败");

        assert_eq!(appender.max_size(), Some(10 * 1024 * 1024));
    }

    // ========================================
    // AC-10.1.1: 基础创建和写入测试
    // ========================================

    /// UT-10.1-001: 基础创建测试
    #[test]
    fn test_basic_creation() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("basic.log");

        let appender = RotatingFileAppender::new(&log_path, 1024 * 1024).expect("创建失败");

        assert_eq!(appender.path(), log_path.as_path());
        assert_eq!(appender.max_size(), Some(1024 * 1024));
        assert_eq!(appender.level(), Level::Trace);
        assert_eq!(appender.current_size(), 0);
        assert_eq!(appender.rotation_period(), RotationPeriod::None);
    }

    /// UT-10.1-002: 基础写入测试
    #[test]
    fn test_basic_write() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("write.log");

        let appender = RotatingFileAppender::new(&log_path, 1024 * 1024).expect("创建失败");
        let event = LogEvent::new(Level::Info, "test", "Hello, rotation!");

        appender.write(&event).expect("写入失败");
        appender.flush().expect("刷新失败");

        let content = std::fs::read_to_string(&log_path).expect("读取失败");
        assert!(content.contains("Hello, rotation!"));
    }

    // ========================================
    // AC-10.1.8: 初始化时文件大小检测
    // ========================================

    /// UT-10.1-010: 初始化时读取现有文件大小 (AC-10.1.8 #14)
    #[test]
    fn test_init_reads_existing_file_size() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("existing.log");

        // 创建一个包含内容的文件
        let initial_content = "existing content\n";
        std::fs::write(&log_path, initial_content).expect("写入初始内容失败");

        // 创建 RotatingFileAppender
        let appender = RotatingFileAppender::new(&log_path, 1024 * 1024).expect("创建失败");

        // 验证 current_size 被正确设置
        assert_eq!(appender.current_size(), initial_content.len() as u64);
    }

    // ========================================
    // AC-10.1.1, AC-10.1.2: with_level 和 with_formatter 测试
    // ========================================

    /// UT-10.1-008: with_level() builder 方法测试
    #[test]
    fn test_with_level() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("level.log");

        let appender = RotatingFileAppender::new(&log_path, 1024)
            .expect("创建失败")
            .with_level(Level::Warn);

        assert_eq!(appender.level(), Level::Warn);

        // 写入低于阈值的日志应被过滤
        let info_event = LogEvent::new(Level::Info, "test", "info message");
        appender.write(&info_event).expect("写入失败");
        appender.flush().expect("刷新失败");

        let content = std::fs::read_to_string(&log_path).expect("读取失败");
        assert!(content.is_empty(), "INFO 日志应被过滤");

        // 写入等于阈值的日志应通过
        let warn_event = LogEvent::new(Level::Warn, "test", "warn message");
        appender.write(&warn_event).expect("写入失败");
        appender.flush().expect("刷新失败");

        let content = std::fs::read_to_string(&log_path).expect("读取失败");
        assert!(content.contains("warn message"), "WARN 日志应通过");
    }

    /// UT-10.1-012: with_formatter() builder 方法测试
    #[test]
    fn test_with_formatter() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("formatter.log");

        let appender = RotatingFileAppender::new(&log_path, 1024)
            .expect("创建失败")
            .with_formatter(Box::new(TextFormatter::new()));

        let event = LogEvent::new(Level::Info, "test", "formatted message");
        appender.write(&event).expect("写入失败");
        appender.flush().expect("刷新失败");

        let content = std::fs::read_to_string(&log_path).expect("读取失败");
        assert!(content.contains("formatted message"));
    }

    // ========================================
    // AC-10.1.2: 轮转文件命名测试
    // ========================================

    /// UT-10.1-007: 轮转文件命名格式测试 (AC-10.1.2 #3)
    #[test]
    fn test_generate_rotated_filename_with_extension() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        let appender = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");

        let rotated = appender.generate_rotated_filename("2025-12-07", 1);
        let expected = temp_dir.path().join("app.2025-12-07.1.log");

        assert_eq!(rotated, expected);
    }

    /// UT-10.1-013: 无扩展名文件轮转命名测试 (AC-10.1.2 #5)
    #[test]
    fn test_generate_rotated_filename_without_extension() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app");

        let appender = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");

        let rotated = appender.generate_rotated_filename("2025-12-07", 1);
        let expected = temp_dir.path().join("app.2025-12-07.1");

        assert_eq!(rotated, expected);
    }

    /// UT-10.1-009: 同一天多次轮转 seq 递增测试 (AC-10.1.2 #4)
    #[test]
    fn test_get_max_seq_for_date() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        // 创建一些轮转文件
        std::fs::write(temp_dir.path().join("app.2025-12-07.1.log"), "").unwrap();
        std::fs::write(temp_dir.path().join("app.2025-12-07.2.log"), "").unwrap();
        std::fs::write(temp_dir.path().join("app.2025-12-07.3.log"), "").unwrap();

        let appender = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");

        let max_seq = appender.get_max_seq_for_date("2025-12-07");
        assert_eq!(max_seq, 3);

        // 不同日期应返回 0
        let max_seq_other = appender.get_max_seq_for_date("2025-12-08");
        assert_eq!(max_seq_other, 0);
    }

    // ========================================
    // AC-10.1.1: 大小触发轮转测试
    // ========================================

    /// IT-10.1-001: 完整轮转流程测试 (AC-10.1.1 #1, #2)
    #[test]
    fn test_size_based_rotation() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("rotate.log");

        // 使用很小的 max_size 触发轮转
        let appender = RotatingFileAppender::new(&log_path, 100).expect("创建失败");

        // 写入足够多的日志触发轮转
        for i in 0..10 {
            let event = LogEvent::new(Level::Info, "test", format!("Message {}", i));
            appender.write(&event).expect("写入失败");
        }
        appender.flush().expect("刷新失败");

        // 验证轮转文件存在
        let entries: Vec<_> = std::fs::read_dir(temp_dir.path())
            .unwrap()
            .filter_map(|e| e.ok())
            .filter(|e| {
                e.file_name()
                    .to_str()
                    .map(|s| s.starts_with("rotate."))
                    .unwrap_or(false)
            })
            .collect();

        // 应该有多个文件（原文件 + 轮转文件）
        assert!(entries.len() >= 2, "应该有轮转文件生成");
    }

    // ========================================
    // AC-10.1.4: 线程安全测试
    // ========================================

    /// IT-10.1-002: 多线程并发轮转测试 (AC-10.1.4 #7)
    #[test]
    fn test_concurrent_writes_with_rotation() {
        use std::sync::Arc;
        use std::thread;

        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("concurrent.log");

        // 使用较小的 max_size 触发轮转，禁用清理以保留所有文件
        let appender = Arc::new(
            RotatingFileAppender::new(&log_path, 500)
                .expect("创建失败")
                .with_max_files(0), // 禁用清理
        );
        let mut handles = vec![];

        // 多线程并发写入
        for i in 0..4 {
            let appender_clone = Arc::clone(&appender);
            let handle = thread::spawn(move || {
                for j in 0..25 {
                    let event = LogEvent::new(
                        Level::Info,
                        "concurrent_test",
                        format!("Thread {} - Message {}", i, j),
                    );
                    appender_clone.write(&event).expect("写入失败");
                }
            });
            handles.push(handle);
        }

        // 等待所有线程完成
        for handle in handles {
            handle.join().expect("线程 join 失败");
        }
        appender.flush().expect("刷新失败");

        // 验证没有 panic，所有日志都被写入
        // 统计所有文件中的日志行数
        let mut total_lines = 0;
        for entry in std::fs::read_dir(temp_dir.path()).unwrap().flatten() {
            if entry
                .file_name()
                .to_str()
                .map(|s| s.contains("concurrent"))
                .unwrap_or(false)
            {
                let content = std::fs::read_to_string(entry.path()).unwrap_or_default();
                total_lines += content.lines().count();
            }
        }

        assert_eq!(total_lines, 100, "应有 100 条日志");
    }

    // ========================================
    // 目录自动创建测试
    // ========================================

    /// UT-10.1-003: 目录自动创建测试
    #[test]
    fn test_directory_auto_create() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let nested_path = temp_dir.path().join("subdir").join("app.log");
        assert!(!nested_path.parent().unwrap().exists(), "目录不应存在");

        let appender = RotatingFileAppender::new(&nested_path, 1024).expect("创建失败");
        let event = LogEvent::new(Level::Info, "test", "message");
        appender.write(&event).expect("写入失败");
        appender.flush().expect("刷新失败");

        assert!(nested_path.parent().unwrap().exists(), "目录应被创建");
        assert!(nested_path.exists(), "文件应被创建");
    }

    // ========================================
    // flush 和 close 测试
    // ========================================

    /// UT-10.1-004: flush 方法测试
    #[test]
    fn test_flush() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("flush.log");

        let appender = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");
        let event = LogEvent::new(Level::Info, "test", "flush test");

        appender.write(&event).expect("写入失败");
        let result = appender.flush();

        assert!(result.is_ok());

        let content = std::fs::read_to_string(&log_path).expect("读取失败");
        assert!(content.contains("flush test"));
    }

    /// UT-10.1-014: close 方法测试
    #[test]
    fn test_close() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("close.log");

        let appender = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");
        let event = LogEvent::new(Level::Info, "test", "close test");

        appender.write(&event).expect("写入失败");
        let result = appender.close();

        assert!(result.is_ok());

        let content = std::fs::read_to_string(&log_path).expect("读取失败");
        assert!(content.contains("close test"));
    }

    // ========================================
    // Story 10.2: 时间轮转测试
    // ========================================

    /// UT-10.2-007: is_same_period() Hourly 测试 (AC-10.2.3 #8)
    #[test]
    fn test_is_same_period_hourly() {
        use time::macros::datetime;

        // 同一小时
        let t1 = datetime!(2025-12-07 14:30:00 UTC);
        let t2 = datetime!(2025-12-07 14:45:00 UTC);
        assert!(RotatingFileAppender::is_same_period(
            t1,
            t2,
            RotationPeriod::Hourly
        ));

        // 不同小时
        let t3 = datetime!(2025-12-07 15:00:00 UTC);
        assert!(!RotatingFileAppender::is_same_period(
            t1,
            t3,
            RotationPeriod::Hourly
        ));
    }

    /// UT-10.2-008: is_same_period() Daily 测试 (AC-10.2.3 #7)
    #[test]
    fn test_is_same_period_daily() {
        use time::macros::datetime;

        // 同一天
        let t1 = datetime!(2025-12-07 14:30:00 UTC);
        let t2 = datetime!(2025-12-07 23:59:59 UTC);
        assert!(RotatingFileAppender::is_same_period(
            t1,
            t2,
            RotationPeriod::Daily
        ));

        // 不同天
        let t3 = datetime!(2025-12-08 00:00:00 UTC);
        assert!(!RotatingFileAppender::is_same_period(
            t1,
            t3,
            RotationPeriod::Daily
        ));
    }

    /// UT-10.2-009: is_same_period() Weekly 测试 (AC-10.2.3 #9)
    #[test]
    fn test_is_same_period_weekly() {
        use time::macros::datetime;

        // 同一周（2025-12-07 是周日，2025-12-08 是周一，新的一周）
        let t1 = datetime!(2025-12-07 14:30:00 UTC); // 周日
        let t2 = datetime!(2025-12-07 23:59:59 UTC); // 周日
        assert!(RotatingFileAppender::is_same_period(
            t1,
            t2,
            RotationPeriod::Weekly
        ));

        // 不同周（周一是新的一周）
        let t3 = datetime!(2025-12-08 00:00:00 UTC); // 周一
        assert!(!RotatingFileAppender::is_same_period(
            t1,
            t3,
            RotationPeriod::Weekly
        ));
    }

    /// UT-10.2-010: 时间轮转文件命名 Daily (AC-10.2.7 #16)
    #[test]
    fn test_get_date_prefix_daily() {
        use time::macros::datetime;

        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        let appender = RotatingFileAppender::with_time_rotation(&log_path, RotationPeriod::Daily)
            .expect("创建失败");

        let time = datetime!(2025-12-07 14:30:00 UTC);
        let prefix = appender.get_date_prefix(time);
        assert_eq!(prefix, "2025-12-07");
    }

    /// UT-10.2-011: 时间轮转文件命名 Hourly (AC-10.2.7 #17)
    #[test]
    fn test_get_date_prefix_hourly() {
        use time::macros::datetime;

        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        let appender = RotatingFileAppender::with_time_rotation(&log_path, RotationPeriod::Hourly)
            .expect("创建失败");

        let time = datetime!(2025-12-07 14:30:00 UTC);
        let prefix = appender.get_date_prefix(time);
        assert_eq!(prefix, "2025-12-07-14");
    }

    /// UT-10.2-012: 时间轮转文件命名 Weekly (AC-10.2.7 #18)
    #[test]
    fn test_get_date_prefix_weekly() {
        use time::macros::datetime;

        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        let appender = RotatingFileAppender::with_time_rotation(&log_path, RotationPeriod::Weekly)
            .expect("创建失败");

        let time = datetime!(2025-12-07 14:30:00 UTC);
        let prefix = appender.get_date_prefix(time);
        // 2025-12-07 是第 49 周
        assert_eq!(prefix, "2025-W49");
    }

    /// UT-10.2-013: with_rotation_period() builder 方法测试 (AC-10.2.8 #19)
    #[test]
    fn test_with_rotation_period() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        let appender = RotatingFileAppender::new(&log_path, 1024)
            .expect("创建失败")
            .with_rotation_period(RotationPeriod::Daily);

        assert_eq!(appender.rotation_period(), RotationPeriod::Daily);
        assert_eq!(appender.max_size(), Some(1024));
    }

    /// UT-10.2-014: with_time_rotation() 构造函数测试 (AC-10.2.12 #24)
    #[test]
    fn test_with_time_rotation_constructor() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        let appender = RotatingFileAppender::with_time_rotation(&log_path, RotationPeriod::Daily)
            .expect("创建失败");

        assert_eq!(appender.rotation_period(), RotationPeriod::Daily);
        assert!(appender.max_size().is_none()); // 仅时间轮转，无大小限制
    }

    /// UT-10.2-015: with_config() 构造函数测试 - 双触发 (AC-10.2.4 #10, #11)
    #[test]
    fn test_with_config_dual_trigger() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        let appender = RotatingFileAppender::with_config(
            &log_path,
            Some(10 * 1024 * 1024),
            RotationPeriod::Daily,
        )
        .expect("创建失败");

        assert_eq!(appender.rotation_period(), RotationPeriod::Daily);
        assert_eq!(appender.max_size(), Some(10 * 1024 * 1024));
    }

    /// UT-10.2-016: should_rotate_by_time() 测试
    #[test]
    fn test_should_rotate_by_time() {
        use time::macros::datetime;

        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        let appender = RotatingFileAppender::with_time_rotation(&log_path, RotationPeriod::Daily)
            .expect("创建失败");

        // 设置 last_rotation_time 为昨天
        let yesterday = datetime!(2025-12-06 14:30:00 UTC);
        appender.set_last_rotation_time_for_test(yesterday);

        // 今天应该需要轮转
        let today = datetime!(2025-12-07 14:30:00 UTC);
        assert!(appender.should_rotate_by_time(today));

        // 同一天不需要轮转
        let same_day = datetime!(2025-12-06 23:59:59 UTC);
        assert!(!appender.should_rotate_by_time(same_day));
    }

    /// UT-10.2-017: RotationPeriod::None 不触发时间轮转
    #[test]
    fn test_rotation_period_none_no_time_rotation() {
        use time::macros::datetime;

        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        // 使用 None 周期
        let appender =
            RotatingFileAppender::with_config(&log_path, Some(1024), RotationPeriod::None)
                .expect("创建失败");

        // 设置 last_rotation_time 为很久以前
        let old_time = datetime!(2020-01-01 00:00:00 UTC);
        appender.set_last_rotation_time_for_test(old_time);

        // 即使时间差很大，也不应该触发时间轮转
        let now = datetime!(2025-12-07 14:30:00 UTC);
        assert!(!appender.should_rotate_by_time(now));
    }

    // ========================================
    // Story 10.3: 历史文件数量限制测试
    // ========================================

    /// UT-10.3-001: list_rotated_files() 基础扫描 (AC-10.3.2 #4)
    #[test]
    fn test_list_rotated_files_basic() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        // 创建一些轮转文件
        std::fs::write(temp_dir.path().join("app.2025-12-05.1.log"), "").unwrap();
        std::fs::write(temp_dir.path().join("app.2025-12-06.1.log"), "").unwrap();
        std::fs::write(temp_dir.path().join("app.2025-12-07.1.log"), "").unwrap();
        // 创建当前活动文件
        std::fs::write(&log_path, "").unwrap();

        let appender = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");
        let files = appender.list_rotated_files();

        // 应该找到 3 个轮转文件（不含当前活动文件）
        assert_eq!(files.len(), 3);
    }

    /// UT-10.3-002: 文件模式匹配（有扩展名）(AC-10.3.3 #7)
    #[test]
    fn test_list_rotated_files_with_extension() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        // 创建匹配的轮转文件
        std::fs::write(temp_dir.path().join("app.2025-12-07.1.log"), "").unwrap();
        // 创建不匹配的文件
        std::fs::write(temp_dir.path().join("other.2025-12-07.1.log"), "").unwrap();
        std::fs::write(temp_dir.path().join("app.txt"), "").unwrap();

        let appender = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");
        let files = appender.list_rotated_files();

        // 应该只找到 1 个匹配的文件
        assert_eq!(files.len(), 1);
        assert!(files[0]
            .file_name()
            .unwrap()
            .to_str()
            .unwrap()
            .starts_with("app.2025-12-07"));
    }

    /// UT-10.3-003: 文件模式匹配（无扩展名）(AC-10.3.3 #8)
    #[test]
    fn test_list_rotated_files_without_extension() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app");

        // 创建匹配的轮转文件（无扩展名）
        std::fs::write(temp_dir.path().join("app.2025-12-07.1"), "").unwrap();
        std::fs::write(temp_dir.path().join("app.2025-12-07.2"), "").unwrap();

        let appender = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");
        let files = appender.list_rotated_files();

        assert_eq!(files.len(), 2);
    }

    /// UT-10.3-004: 压缩文件包含在匹配结果中 (AC-10.3.3 #9)
    #[test]
    fn test_list_rotated_files_includes_compressed() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        // 创建普通轮转文件和压缩文件
        std::fs::write(temp_dir.path().join("app.2025-12-05.1.log"), "").unwrap();
        std::fs::write(temp_dir.path().join("app.2025-12-06.1.log.gz"), "").unwrap();
        std::fs::write(temp_dir.path().join("app.2025-12-07.1.log.zst"), "").unwrap();

        let appender = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");
        let files = appender.list_rotated_files();

        // 应该找到 3 个文件（包括压缩文件）
        assert_eq!(files.len(), 3);
    }

    /// UT-10.3-007: cleanup_old_files() 删除逻辑 (AC-10.3.1 #1, #2)
    #[test]
    fn test_cleanup_old_files_deletes_oldest() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        // 创建 5 个轮转文件
        for i in 1..=5 {
            let file_path = temp_dir.path().join(format!("app.2025-12-0{}.1.log", i));
            std::fs::write(&file_path, format!("content {}", i)).unwrap();
            // 设置不同的修改时间（通过短暂延迟）
            std::thread::sleep(std::time::Duration::from_millis(10));
        }

        // 创建 appender，max_files = 3
        let appender = RotatingFileAppender::new(&log_path, 1024)
            .expect("创建失败")
            .with_max_files(3);

        // 执行清理
        appender.cleanup_old_files();

        // 应该只剩下 3 个文件
        let remaining_files = appender.list_rotated_files();
        assert_eq!(remaining_files.len(), 3);
    }

    /// UT-10.3-008: max_files = 0 禁用清理 (AC-10.3.1 #3)
    #[test]
    fn test_cleanup_disabled_when_max_files_zero() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        // 创建 10 个轮转文件
        for i in 1..=10 {
            let file_path = temp_dir.path().join(format!("app.2025-12-{:02}.1.log", i));
            std::fs::write(&file_path, "").unwrap();
        }

        // 创建 appender，max_files = 0（禁用清理）
        let appender = RotatingFileAppender::new(&log_path, 1024)
            .expect("创建失败")
            .with_max_files(0);

        // 执行清理
        appender.cleanup_old_files();

        // 所有文件应该都保留
        let remaining_files = appender.list_rotated_files();
        assert_eq!(remaining_files.len(), 10);
    }

    /// UT-10.3-009: max_files = None 时不执行清理 (Story 10-1 行为)
    ///
    /// 注意：为避免日志丢失，未配置 max_files 时不执行清理。
    /// 如需启用清理，必须显式设置 max_files 值。
    #[test]
    fn test_cleanup_uses_default_max_files() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        // 创建 10 个轮转文件
        for i in 1..=10 {
            let file_path = temp_dir.path().join(format!("app.2025-12-{:02}.1.log", i));
            std::fs::write(&file_path, "").unwrap();
            std::thread::sleep(std::time::Duration::from_millis(10));
        }

        // 创建 appender，不设置 max_files
        let appender = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");
        assert!(appender.max_files().is_none()); // 未显式设置

        // 执行清理
        appender.cleanup_old_files();

        // 未配置 max_files 时不执行清理，所有文件应保留
        let remaining_files = appender.list_rotated_files();
        assert_eq!(remaining_files.len(), 10);
    }

    /// UT-10.3-009b: 显式设置 max_files = 7 时执行清理
    #[test]
    fn test_cleanup_with_explicit_max_files() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        // 创建 10 个轮转文件
        for i in 1..=10 {
            let file_path = temp_dir.path().join(format!("app.2025-12-{:02}.1.log", i));
            std::fs::write(&file_path, "").unwrap();
            std::thread::sleep(std::time::Duration::from_millis(10));
        }

        // 创建 appender，显式设置 max_files = 7
        let appender = RotatingFileAppender::new(&log_path, 1024)
            .expect("创建失败")
            .with_max_files(7);
        assert_eq!(appender.max_files(), Some(7));

        // 执行清理
        appender.cleanup_old_files();

        // 应该只剩下 7 个文件
        let remaining_files = appender.list_rotated_files();
        assert_eq!(remaining_files.len(), 7);
    }

    /// UT-10.3-010: with_max_files() builder 方法 (AC-10.3.8 #18, #22)
    #[test]
    fn test_with_max_files_builder() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        let appender = RotatingFileAppender::new(&log_path, 1024)
            .expect("创建失败")
            .with_max_files(30);

        assert_eq!(appender.max_files(), Some(30));
    }

    /// UT-10.3-011: max_files() getter 方法 (AC-10.3.10 #21)
    #[test]
    fn test_max_files_getter() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        // 未设置时返回 None
        let appender1 = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");
        assert!(appender1.max_files().is_none());

        // 设置后返回 Some
        let appender2 = RotatingFileAppender::new(temp_dir.path().join("app2.log"), 1024)
            .expect("创建失败")
            .with_max_files(5);
        assert_eq!(appender2.max_files(), Some(5));
    }

    /// UT-10.3-015: max_files = 1 边界条件 (AC-10.3.1 #1)
    #[test]
    fn test_cleanup_max_files_one() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        // 创建 5 个轮转文件
        for i in 1..=5 {
            let file_path = temp_dir.path().join(format!("app.2025-12-0{}.1.log", i));
            std::fs::write(&file_path, "").unwrap();
            std::thread::sleep(std::time::Duration::from_millis(10));
        }

        // 创建 appender，max_files = 1
        let appender = RotatingFileAppender::new(&log_path, 1024)
            .expect("创建失败")
            .with_max_files(1);

        // 执行清理
        appender.cleanup_old_files();

        // 应该只剩下 1 个文件
        let remaining_files = appender.list_rotated_files();
        assert_eq!(remaining_files.len(), 1);
    }

    /// UT-10.3-016: 跳过当前活动文件 (AC-10.3.3 #7)
    #[test]
    fn test_list_rotated_files_skips_active_file() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        // 创建当前活动文件
        std::fs::write(&log_path, "active content").unwrap();
        // 创建轮转文件
        std::fs::write(temp_dir.path().join("app.2025-12-07.1.log"), "").unwrap();

        let appender = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");
        let files = appender.list_rotated_files();

        // 应该只找到 1 个轮转文件（不含当前活动文件）
        assert_eq!(files.len(), 1);
        assert!(!files.iter().any(|f| f == &log_path));
    }

    /// UT-10.3-018: with_full_config() 构造函数 (AC-10.3.10 #21)
    #[test]
    fn test_with_full_config_max_files() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        let appender = RotatingFileAppender::with_full_config(
            &log_path,
            Some(10 * 1024 * 1024),
            RotationPeriod::Daily,
            Some(30),
        )
        .expect("创建失败");

        assert_eq!(appender.max_files(), Some(30));
        assert_eq!(appender.max_size(), Some(10 * 1024 * 1024));
        assert_eq!(appender.rotation_period(), RotationPeriod::Daily);
    }

    // ========================================
    // Story 10.4: 轮转文件命名测试
    // ========================================

    /// UT-10.4-001: 多点文件名轮转命名 (AC-10.4.5 #13)
    ///
    /// 验证文件名包含多个点时（如 `app.service.log`），
    /// 正确识别 stem 为 `app.service`，ext 为 `log`
    #[test]
    fn test_multi_dot_filename_rotation() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.service.log");

        let appender = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");
        let rotated = appender.generate_rotated_filename("2025-12-08", 1);

        // 验证 stem = "app.service", ext = "log"
        let expected = temp_dir.path().join("app.service.2025-12-08.1.log");
        assert_eq!(rotated, expected);
    }

    /// UT-10.4-002: 新一天序号重置为 1 (AC-10.4.2 #5)
    ///
    /// 验证跨天后序号从 1 开始
    #[test]
    fn test_new_day_seq_resets_to_one() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        // 创建昨天的轮转文件
        std::fs::write(temp_dir.path().join("app.2025-12-07.1.log"), "").unwrap();
        std::fs::write(temp_dir.path().join("app.2025-12-07.2.log"), "").unwrap();
        std::fs::write(temp_dir.path().join("app.2025-12-07.3.log"), "").unwrap();

        let appender = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");

        // 昨天的最大序号应该是 3
        let max_seq_yesterday = appender.get_max_seq_for_date("2025-12-07");
        assert_eq!(max_seq_yesterday, 3);

        // 今天（新的一天）应该返回 0，下一个序号将是 1
        let max_seq_today = appender.get_max_seq_for_date("2025-12-08");
        assert_eq!(max_seq_today, 0);

        // 生成今天的第一个轮转文件名
        let rotated = appender.generate_rotated_filename("2025-12-08", max_seq_today + 1);
        let expected = temp_dir.path().join("app.2025-12-08.1.log");
        assert_eq!(rotated, expected);
    }

    /// UT-10.4-003: Weekly ISO week 边界测试 (AC-10.4.3 #8)
    ///
    /// 验证 2024-12-30 (周一) 属于 ISO week 2025-W01
    #[test]
    fn test_weekly_iso_week_boundary() {
        use time::macros::datetime;

        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        let appender = RotatingFileAppender::with_time_rotation(&log_path, RotationPeriod::Weekly)
            .expect("创建失败");

        // 2024-12-30 (周一) 属于 ISO week 2025-W01
        let time = datetime!(2024-12-30 12:00:00 UTC);
        let prefix = appender.get_date_prefix(time);
        assert_eq!(prefix, "2025-W01");

        // 2024-12-29 (周日) 属于 ISO week 2024-W52
        let time_sunday = datetime!(2024-12-29 12:00:00 UTC);
        let prefix_sunday = appender.get_date_prefix(time_sunday);
        assert_eq!(prefix_sunday, "2024-W52");
    }

    /// UT-10.4-004: 同一天多次轮转序号递增 (AC-10.4.2 #3, #4)
    #[test]
    fn test_same_day_seq_increments() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        // 创建同一天的多个轮转文件
        std::fs::write(temp_dir.path().join("app.2025-12-08.1.log"), "").unwrap();
        std::fs::write(temp_dir.path().join("app.2025-12-08.2.log"), "").unwrap();
        std::fs::write(temp_dir.path().join("app.2025-12-08.3.log"), "").unwrap();

        let appender = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");

        // 最大序号应该是 3
        let max_seq = appender.get_max_seq_for_date("2025-12-08");
        assert_eq!(max_seq, 3);

        // 下一个轮转文件应该是 seq = 4
        let rotated = appender.generate_rotated_filename("2025-12-08", max_seq + 1);
        let expected = temp_dir.path().join("app.2025-12-08.4.log");
        assert_eq!(rotated, expected);
    }

    /// UT-10.4-005: 文件名排序验证 (AC-10.4.4 #10, #11, #12)
    ///
    /// 验证清理使用修改时间排序，而非文件名字典序
    #[test]
    fn test_cleanup_uses_modification_time_not_filename() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let log_path = temp_dir.path().join("app.log");

        // 创建文件，按修改时间顺序（最旧到最新）
        // 注意：文件名字典序与修改时间顺序不同
        let files = [
            "app.2025-12-08.10.log", // 字典序靠前，但创建较晚
            "app.2025-12-08.2.log",  // 字典序靠后，但创建较早
            "app.2025-12-08.1.log",  // 字典序最后，但创建最早
        ];

        for (i, filename) in files.iter().enumerate() {
            let file_path = temp_dir.path().join(filename);
            std::fs::write(&file_path, format!("content {}", i)).unwrap();
            // 确保修改时间不同
            std::thread::sleep(std::time::Duration::from_millis(20));
        }

        // 创建 appender，max_files = 1
        let appender = RotatingFileAppender::new(&log_path, 1024)
            .expect("创建失败")
            .with_max_files(1);

        // 执行清理
        appender.cleanup_old_files();

        // 应该只剩下 1 个文件（最新的）
        let remaining_files = appender.list_rotated_files();
        assert_eq!(remaining_files.len(), 1);

        // 剩下的应该是最后创建的文件（app.2025-12-08.1.log）
        let remaining_name = remaining_files[0].file_name().unwrap().to_str().unwrap();
        assert_eq!(remaining_name, "app.2025-12-08.1.log");
    }

    /// UT-10.4-006: 各周期命名格式验证 (AC-10.4.3 #6, #7, #8, #9)
    #[test]
    fn test_all_period_naming_formats() {
        use time::macros::datetime;

        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let time = datetime!(2025-12-08 14:30:00 UTC);

        // Daily: YYYY-MM-DD
        let daily_path = temp_dir.path().join("daily.log");
        let daily_appender =
            RotatingFileAppender::with_time_rotation(&daily_path, RotationPeriod::Daily)
                .expect("创建失败");
        assert_eq!(daily_appender.get_date_prefix(time), "2025-12-08");

        // Hourly: YYYY-MM-DD-HH
        let hourly_path = temp_dir.path().join("hourly.log");
        let hourly_appender =
            RotatingFileAppender::with_time_rotation(&hourly_path, RotationPeriod::Hourly)
                .expect("创建失败");
        assert_eq!(hourly_appender.get_date_prefix(time), "2025-12-08-14");

        // Weekly: YYYY-Www
        let weekly_path = temp_dir.path().join("weekly.log");
        let weekly_appender =
            RotatingFileAppender::with_time_rotation(&weekly_path, RotationPeriod::Weekly)
                .expect("创建失败");
        assert_eq!(weekly_appender.get_date_prefix(time), "2025-W50");

        // Size-only (None): 复用 Daily 格式 YYYY-MM-DD
        let size_path = temp_dir.path().join("size.log");
        let size_appender = RotatingFileAppender::new(&size_path, 1024).expect("创建失败");
        assert_eq!(size_appender.get_date_prefix(time), "2025-12-08");
    }

    /// UT-10.4-007: 轮转文件在同一目录 (AC-10.4.5 #14)
    #[test]
    fn test_rotated_file_in_same_directory() {
        let temp_dir = TempDir::new().expect("创建临时目录失败");
        let subdir = temp_dir.path().join("logs");
        std::fs::create_dir_all(&subdir).expect("创建子目录失败");

        let log_path = subdir.join("app.log");
        let appender = RotatingFileAppender::new(&log_path, 1024).expect("创建失败");

        let rotated = appender.generate_rotated_filename("2025-12-08", 1);

        // 轮转文件应该在同一目录
        assert_eq!(rotated.parent(), log_path.parent());
        assert_eq!(rotated, subdir.join("app.2025-12-08.1.log"));
    }
}
